abstract type ResultDict{R, S} <: AbstractDict{R, S} end

function Base.show(io::IO, r::ResultDict)
    if !isempty(r.internals)
        print(io, "<$(nameof(typeof(r))): $(keys(r.toplevel)), internals=$(keys(r.internals))>")
    else
        print(io, "<$(nameof(typeof(r))): $(keys(r.toplevel)))>")
    end 
end

Base.keytype(r::ResultDict{R, S}) where {R, S} = R
Base.valtype(r::ResultDict{R, S}) where {R, S} = S
    
Base.iterate(r::ResultDict) = iterate(r.toplevel)
Base.iterate(r::ResultDict, i) = iterate(r.toplevel, i)

Base.getindex(r::ResultDict, k::String; kwargs...) = r.toplevel[k]
Base.getindex(r::ResultDict, k::Tuple; kwargs...) = error("Key $k to $(nameof(typeof(r))) cannot be a tuple")
function Base.getindex(r::ResultDict, k; kwargs...)
    try
        return getfield(Main, nameof(typeof(r)))(Dict(ki => r.toplevel[ki] for ki ∈ k); kwargs...)
    catch
        error("Key $k to $(nameof(typeof(r))) needs to be a string or an iterable of strings")
    end
end

Base.setindex!(r::ResultDict, v, k) = r.toplevel[k] = v

function Base.:*(r::ResultDict, x::Bijection)
    newr = deepcopy(r)
    newr.toplevel = x * r.toplevel
    return newr
end
Base.:*(x::Bijection, r::ResultDict) = r * x

Base.length(r::ResultDict) = length(r.toplevel)

function Base.union(r::ResultDict, s::ResultDict)
    if nameof(typeof(r)) != nameof(typeof(s))
        error("Trying to merge a $(nameof(typeof(r))) with a $(nameof(typeof(s))).")
    end
    merged = copy(r)
    merge!(merged, s)
    return merged
end

Base.keys(r::ResultDict) = keys(r.toplevel)
Base.values(r::ResultDict) = values(r.toplevel)
Base.pairs(r::ResultDict) = pairs(r.toplevel)

function Base.merge!(r::ResultDict, s::ResultDict)
    merge!(r.toplevel, s.toplevel)
    merge!(r.internals, s.internals)
end

Base.merge!(r::ResultDict, s::AbstractDict) = merge!(r.toplevel, Dict(s))
Base.merge!(r::ResultDict, s::Any) = merge!(r.toplevel, Dict(s))

Base.copy(r::ResultDict) = getfield(Main, nameof(typeof(r)))(r)
